package main

import "fmt"

// 内存逃逸：函数的出参从栈上"逃到"堆上
/**
在go语言中
1. 形参不存在引用类型，都是值类型
2. 当函数的入参为int string时，传参都直接转换为变量带入
3. 当函数的入参为列表或者对象时候，go会将当前参数中的列表或者对象的元素加入到栈中
   然后依次给函数体中的代码执行；执行完成后，按照栈的形式组装成列表出参
4. 入参和出参皆可以为指针类型的数据
*/

// 定义一个函数，返回一个string类型的指针，返回写在参数列表后面
func ptr() *string {
	name := "小王子"
	p0 := &name
	fmt.Println("*p0: ", *p0)

	city := "上海"
	return &city
}

func main() {
	// 1. 内存逃逸
	pointerRes := ptr()
	fmt.Println("pointerRes: ", pointerRes)  // pointerRes:  0xc000010240
	fmt.Println("pointerRes: ", *pointerRes) // pointerRes:  上海

	// 2. build当前文件，查看gc
	// 命令：
	// go build --gcflags "-m -m -l" main.go
	// 结果如下：

	// # command-line-arguments
	// ./main.go:19:23: *p0 escapes to heap:
	// ./main.go:19:23:   flow: {storage for ... argument} = &{storage for *p0}:
	// ./main.go:19:23:     from *p0 (spill) at ./main.go:19:23
	// ./main.go:19:23:     from ... argument (slice-literal-element) at ./main.go:19:13
	// ./main.go:19:23:   flow: {heap} = {storage for ... argument}:
	// ./main.go:19:23:     from ... argument (spill) at ./main.go:19:13
	// ./main.go:19:23:     from fmt.Println(... argument...) (call parameter) at ./main.go:19:13
	// ./main.go:19:14: "*p0: " escapes to heap:
	// ./main.go:19:14:   flow: {storage for ... argument} = &{storage for "*p0: "}:
	// ./main.go:19:14:     from "*p0: " (spill) at ./main.go:19:14
	// ./main.go:19:14:     from ... argument (slice-literal-element) at ./main.go:19:13
	// ./main.go:19:14:   flow: {heap} = {storage for ... argument}:
	// ./main.go:19:14:     from ... argument (spill) at ./main.go:19:13
	// ./main.go:19:14:     from fmt.Println(... argument...) (call parameter) at ./main.go:19:13
	// ./main.go:21:2: city escapes to heap:
	// ./main.go:21:2:   flow: ~r0 = &city:
	// ./main.go:21:2:     from &city (address-of) at ./main.go:22:9
	// ./main.go:21:2:     from return &city (return) at ./main.go:22:2
	// ./main.go:21:2: moved to heap: city
	// ./main.go:19:13: ... argument does not escape
	// ./main.go:19:14: "*p0: " escapes to heap
	// ./main.go:19:23: *p0 escapes to heap
	// ./main.go:29:30: *pointerRes escapes to heap:
	// ./main.go:29:30:   flow: {storage for ... argument} = &{storage for *pointerRes}:
	// ./main.go:29:30:     from *pointerRes (spill) at ./main.go:29:30
	// ./main.go:29:30:     from ... argument (slice-literal-element) at ./main.go:29:13
	// ./main.go:29:30:   flow: {heap} = {storage for ... argument}:
	// ./main.go:29:30:     from ... argument (spill) at ./main.go:29:13
	// ./main.go:29:30:     from fmt.Println(... argument...) (call parameter) at ./main.go:29:13
	// ./main.go:29:14: "pointerRes: " escapes to heap:
	// ./main.go:29:14:   flow: {storage for ... argument} = &{storage for "pointerRes: "}:
	// ./main.go:29:14:     from "pointerRes: " (spill) at ./main.go:29:14
	// ./main.go:29:14:     from ... argument (slice-literal-element) at ./main.go:29:13
	// ./main.go:29:14:   flow: {heap} = {storage for ... argument}:
	// ./main.go:29:14:     from ... argument (spill) at ./main.go:29:13
	// ./main.go:29:14:     from fmt.Println(... argument...) (call parameter) at ./main.go:29:13
	// ./main.go:28:14: "pointerRes: " escapes to heap:
	// ./main.go:28:14:   flow: {storage for ... argument} = &{storage for "pointerRes: "}:
	// ./main.go:28:14:     from "pointerRes: " (spill) at ./main.go:28:14
	// ./main.go:28:14:     from ... argument (slice-literal-element) at ./main.go:28:13
	// ./main.go:28:14:   flow: {heap} = {storage for ... argument}:
	// ./main.go:28:14:     from ... argument (spill) at ./main.go:28:13
	// ./main.go:28:14:     from fmt.Println(... argument...) (call parameter) at ./main.go:28:13
	// ./main.go:28:13: ... argument does not escape
	// ./main.go:28:14: "pointerRes: " escapes to heap
	// ./main.go:29:13: ... argument does not escape
	// ./main.go:29:14: "pointerRes: " escapes to heap
	// ./main.go:29:30: *pointerRes escapes to heap
}
